﻿using log4net;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;

[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace AB.Common
{
    /// <summary>
    /// 日志
    /// </summary>
    public class ABLog4Net
    {
        //<!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL(低) -->
        /// <summary>
        /// 初始化Log4Net
        /// </summary>
        /// <returns></returns>
        private static ILog InitLog4Net()
        {
            //var logCfg = new FileInfo(AppDomain.CurrentDomain.BaseDirectory + @"bin\log4net.config");
            //XmlConfigurator.ConfigureAndWatch(logCfg);
            //MethodBase.GetCurrentMethod().DeclaringType
            StackTrace trace = new StackTrace();
            StackFrame frame = trace.GetFrame(2);//1代表上级，2代表上上级，以此类推  
            MethodBase method = frame.GetMethod();
            Type classtype = method.ReflectedType;
            ILog log = LogManager.GetLogger(classtype);
            return log;
        }
        /// <summary>
        /// 严重错误
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Fatal(object message, Exception exception = null)
        {
            ILog log = InitLog4Net();
            if (exception != null)
            {
                log.Fatal(message, exception);
            }
            else
            {
                log.Fatal(message);
            }
        }
        /// <summary>
        /// 错误
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Error(object message, Exception exception = null)
        {
            ILog log = InitLog4Net();
            if (exception != null)
            {
                log.Error(message, exception);
            }
            else
            {
                log.Error(message);
            }
        }
        /// <summary>
        /// 警告
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Warn(object message, Exception exception = null)
        {
            ILog log = InitLog4Net();
            if (exception != null)
            {
                log.Warn(message, exception);
            }
            else
            {
                log.Warn(message);
            }
        }
        /// <summary>
        /// 一般
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Info(object message, Exception exception = null)
        {
            ILog log = InitLog4Net();
            if (exception != null)
            {
                log.Info(message, exception);
            }
            else
            {
                log.Info(message);
            }
        }
        /// <summary>
        /// 调试
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Debug(object message, Exception exception = null)
        {
            ILog log = InitLog4Net();
            if (exception != null)
            {
                log.Debug(message, exception);
            }
            else
            {
                log.Debug(message);
            }
        }
    }
    /// <summary>
    /// 日志
    /// </summary>
    public class ABLog
    {
        //配置文件参数
        //<!--日志路径（为空时默认程序路径/log）-->
        //<add key = "ABLOGPATH" value="log"/>
        //<!--记录日志级别 OFF=0 > FATAL=1 > ERROR=2 > WARN=3 > INFO=4 > DEBUG=5-->
        //<add key = "ABLOGLEVENL" value="0"/>


        //在网站根目录下创建日志目录
        //static string LogPath = HttpContext.Current.Request.PhysicalApplicationPath + "logs";
        //OFF > FATAL > ERROR > WARN > INFO > DEBUG  > ALL
        //  0     1       2       3      4      5
        static int LogLevelConfig = ConfigHelper.IntConfig("ABLOGLEVENL", 0);
        /// <summary>
        /// 日志队列
        /// </summary>
        static Dictionary<LogLevel, Queue<LogInfo>> LogDicQueue = new Dictionary<LogLevel, Queue<LogInfo>>();
        /// <summary>
        /// 写日志线程是否启动
        /// </summary>
        static bool IsRun = false;
        /// <summary>
        /// 获取有关方法和类的信息
        /// </summary>
        /// <returns></returns>
        static MethodInfo GetMethodInfo()
        {
            //取得当前方法命名空间System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace;
            //取得当前方法类全名 包括命名空间System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName;
            //获得当前类名System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
            //取得当前方法名System.Reflection.MethodBase.GetCurrentMethod().Name;
            MethodBase mb = new StackTrace(true).GetFrame(2).GetMethod();
            //取得父方法命名空间mb.DeclaringType.Namespace
            //取得父方法类名mb.DeclaringType.Name
            //取得父方法类全名mb.DeclaringType.FullName
            //取得父方法名mb.Name
            return new MethodInfo(mb);
        }
        /// <summary>
        /// 处理日志(加入队列)
        /// </summary>
        /// <param name="logInfo"></param>
        static void DealLog(LogInfo logInfo)
        {
            ABCommonDebug.WriteLine(logInfo.ToString());
            if (!LogDicQueue.ContainsKey(logInfo.LogLevel))
            {
                LogDicQueue.Add(logInfo.LogLevel, new Queue<LogInfo>());
            }
            LogDicQueue[logInfo.LogLevel].Enqueue(logInfo);
            if (!IsRun) WriteStart();
        }
        /// <summary>
        /// Log文件路径 默认程序路径
        /// </summary>
        /// <returns></returns>
        static string LogPath()
        {
            string assemblyPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
            string defaultPath = Path.GetDirectoryName(assemblyPath);
            string _LogPath = ConfigHelper.StringConfig("ABLOGPATH", "log");
            if (!string.IsNullOrEmpty(_LogPath))
            {
                string path = PathHelper.MyCombine(defaultPath, _LogPath);
                return path;
            }
            return defaultPath;
        }
        /// <summary>
        /// 启动写日志线程
        /// </summary>
        static void WriteStart()
        {
            if (!IsRun)
            {
                IsRun = true;
                Thread t1 = new Thread(new ThreadStart(() =>
                {
                    while (true)
                    {
                        try
                        {
                            bool hasmore = false;
                            foreach (var item in LogDicQueue)
                            {
                                Queue<LogInfo> queue = item.Value;
                                List<LogInfo> loglist = new List<LogInfo>();
                                DateTime? logtime = null;
                                while (loglist.Count < 10 && queue.Count > 0)
                                {
                                    if (logtime == null)
                                    {
                                        LogInfo li = queue.Dequeue();
                                        logtime = li.LogTime.Date;
                                        loglist.Add(li);
                                    }
                                    else
                                    {
                                        LogInfo li = queue.Peek();
                                        if (li.LogTime.Date == logtime)
                                        {
                                            loglist.Add(queue.Dequeue());
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }
                                }
                                if (loglist.Count > 0)
                                {
                                    string filename = LogPath() + "/" + item.Key.ToString() + "/" + ((DateTime)logtime).ToString("yyyy-MM-dd") + ".log";
                                    //创建或打开日志文件，向日志文件末尾追加记录
                                    using (StreamWriter mySw = File.AppendText(filename))
                                    {
                                        foreach (LogInfo logInfo in loglist)
                                        {
                                            //向日志文件写入内容
                                            mySw.WriteLine("@@" + logInfo.LogLevel.ToString() + "====================【" + logInfo.LogTime.ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "】====================");
                                            mySw.WriteLine("【Method】" + logInfo.MethodInfo.FullName + "." + logInfo.MethodInfo.MethodName);
                                            foreach (var content in logInfo.Content)
                                            {
                                                if (content is Exception)
                                                {
                                                    Exception ex = content as Exception;
                                                    mySw.WriteLine("【Message】" + ex.Message);
                                                    mySw.WriteLine("【ErrorType】" + ex.GetType());
                                                    mySw.WriteLine("【TargetSite】" + ex.TargetSite);
                                                    mySw.WriteLine("【Source】" + ex.Source);
                                                    mySw.WriteLine("【StackTrace】" + ex.StackTrace);
                                                }
                                                else if (content != null)
                                                {
                                                    mySw.WriteLine(item.ToString());
                                                }
                                            }
                                        }
                                    }
                                }
                                if (!hasmore && queue.Count > 0)
                                {
                                    hasmore = true;
                                }
                                queue.TrimExcess();
                            }
                            if (!hasmore)
                            {
                                throw new Exception("无日志，休息一下");
                            }
                        }
                        catch (Exception)
                        {
                            Thread.Sleep(2000);
                        }
                    }
                }));
                t1.IsBackground = true;
                t1.Start();
            }
        }
        /// <summary>
        /// FATAL（致命错误）
        /// </summary>
        /// <param name="content"></param>
        public static void Fatal(params object[] content)
        {
            if (LogLevelConfig >= (int)LogLevel.FATAL)
            {
                MethodInfo methodInfo = GetMethodInfo();
                DealLog(new LogInfo(LogLevel.FATAL, methodInfo, content));
            }
        }
        /// <summary>
        /// 如果条件为 true,输出FATAL（致命错误）
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        public static bool FatalIf(bool condition,params object[] content)
        {
            if (condition)
            {
                Fatal(content);
            }
            return condition;
        }
        /// <summary>
        /// ERROR（一般错误）
        /// </summary>
        /// <param name="content"></param>
        public static void Error(params object[] content)
        {
            if (LogLevelConfig >= (int)LogLevel.ERROR)
            {
                MethodInfo methodInfo = GetMethodInfo();
                DealLog(new LogInfo(LogLevel.ERROR, methodInfo, content));
            }
        }
        /// <summary>
        /// 如果条件为 true,输出ERROR（一般错误）
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        public static bool ErrorIf(bool condition, params object[] content)
        {
            if (condition)
            {
                Error(content);
            }
            return condition;
        }
        /// <summary>
        /// WARN（警告）
        /// </summary>
        /// <param name="content"></param>
        public static void Warn(params object[] content)
        {
            if (LogLevelConfig >= (int)LogLevel.WARN)
            {
                MethodInfo methodInfo = GetMethodInfo();
                DealLog(new LogInfo(LogLevel.WARN, methodInfo, content));
            }
        }
        /// <summary>
        /// 如果条件为 true,输出WARN（警告）
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        public static bool WarnIf(bool condition, params object[] content)
        {
            if (condition)
            {
                Warn(content);
            }
            return condition;
        }
        /// <summary>
        /// INFO（一般信息）
        /// </summary>
        /// <param name="content"></param>
        public static void Info(params object[] content)
        {
            if (LogLevelConfig >= (int)LogLevel.INFO)
            {
                MethodInfo methodInfo = GetMethodInfo();
                DealLog(new LogInfo(LogLevel.INFO, methodInfo, content));
            }
        }
        /// <summary>
        /// 如果条件为 true,输出INFO（一般信息）
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        public static bool InfoIf(bool condition, params object[] content)
        {
            if (condition)
            {
                Info(content);
            }
            return condition;
        }
        /// <summary>
        /// DEBUG（调试信息）
        /// </summary>
        /// <param name="content"></param>
        public static void Debug(params object[] content)
        {
            if (LogLevelConfig >= (int)LogLevel.DEBUG)
            {
                MethodInfo methodInfo = GetMethodInfo();
                DealLog(new LogInfo(LogLevel.DEBUG, methodInfo, content));
            }
        }
        /// <summary>
        /// 如果条件为 true,输出DEBUG（调试信息）
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        public static bool DebugIf(bool condition, params object[] content)
        {
            if (condition)
            {
                Debug(content);
            }
            return condition;
        }
    }
    /// <summary>
    /// 相关方法和类的信息
    /// </summary>
    class MethodInfo
    {
        /// <summary>
        /// 命名空间
        /// </summary>
        public string Namespace { get; set; }
        /// <summary>
        /// 类名称
        /// </summary>
        public string ClassName { get; set; }
        /// <summary>
        /// 类的完全限定名
        /// </summary>
        public string FullName { get; set; }
        /// <summary>
        /// 方法名
        /// </summary>
        public string MethodName { get; set; }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="mb">有关方法和类的信息</param>
        public MethodInfo(MethodBase mb)
        {
            Namespace = mb.DeclaringType.Namespace;
            ClassName = mb.DeclaringType.Name;
            FullName = mb.DeclaringType.FullName;
            MethodName = mb.Name;
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        public MethodInfo()
        {

        }
    }
    /// <summary>
    /// 日志信息
    /// </summary>
    class LogInfo
    {
        /// <summary>
        /// 日志等级 FATAL > ERROR > WARN > INFO > DEBUG
        /// </summary>
        public LogLevel LogLevel { get; set; }
        /// <summary>
        /// 与日志相关的方法和类的信息
        /// </summary>
        public MethodInfo MethodInfo { get; set; }
        /// <summary>
        /// 写入日志的数据
        /// </summary>
        public object[] Content { get; set; }
        /// <summary>
        /// 日志记录时间
        /// </summary>
        public DateTime LogTime { get; set; }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logLevel">日志等级</param>
        /// <param name="methodInfo">与日志相关的方法和类的信息</param>
        /// <param name="content">写入日志的数据</param>
        public LogInfo(LogLevel logLevel, MethodInfo methodInfo, params object[] content)
        {
            LogLevel = logLevel;
            MethodInfo = methodInfo;
            Content = content;
            LogTime = DateTime.Now;
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        public LogInfo()
        {

        }
        public override string ToString()
        {
            return this.ABToJSON();
        }
    }
    /// <summary>
    /// 日志等级
    /// </summary>
    enum LogLevel
    {
        FATAL = 1,
        ERROR = 2,
        WARN = 3,
        INFO = 4,
        DEBUG = 5,
    }
}
